//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Hypersonic SQL Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * on behalf of the Hypersonic SQL Group.
 *
 *
 * For work added by the HSQL Development Group:
 *
 * Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;

#if !SILVERLIGHT
using System.Data.Common;
#endif

using System.Threading;
using System.IO;
using EffiProz.Core.Lib;
using EffiProz.Core.Rights;
using EffiProz.Core.Indexes;
using EffiProz.Core.Errors;
using EffiProz.Core.Results;
using EffiProz.Core.Navigators;
using EffiProz.Core.DataTypes;
using EffiProz.Core.Store;
using EffiProz.Core.Persist;


namespace EffiProz.Core
{

    // fredt@users 20020320 - doc 1.7.0 - update
    // fredt@users 20020315 - patch 1.7.0 - switch for scripting
    // fredt@users 20020130 - patch 476694 by velichko - transaction savepoints
    // additions _in different parts to support savepoint transactions
    // fredt@users 20020910 - patch 1.7.1 by fredt - database _readonly enforcement
    // fredt@users 20020912 - patch 1.7.1 by fredt - permanent public connection
    // boucherb@users 20030512 - patch 1.7.2 - compiled statements
    //                                       - session becomes execution hub
    // boucherb@users 20050510 - patch 1.7.2 - generalized Result packet passing
    //                                         based command execution
    //                                       - batch execution handling
    // fredt@users 20030628 - patch 1.7.2 - session proxy support
    // fredt@users 20040509 - patch 1.7.2 - SQL conformance for CURRENT_TIMESTAMP and other datetime functions

    /**
     *  Implementation of a user session with the database. In 1.7.2 Session
     *  becomes the public interface to an HSQLDB database, accessed locally or
     *  remotely via SessionInterface.
     *
     *  When as Session is closed, all references to public engine objects are
     *  set to null. But the session id and scripting mode may still be used for
     *  scripting
     *
     * New class based based on original Hypersonic code.
     * Extensively rewritten and extended _in successive versions of HSQLDB.
     *
     * @author Thomas Mueller (Hypersonic SQL Group)
     * @version 1.8.0
     * @since 1.7.0
     */
    public class Session : SessionInterface
    {
        //
        private volatile bool _isClosed;

        //
        public Database database;
        //private User sessionUser;
        private User user;
        private Grantee role;

        // transaction support
        private volatile bool _isAutoCommit;
        private volatile bool _isReadOnly;
        bool _isReadOnlyDefault;
        int isolationModeDefault = SessionTxConsts.TX_READ_COMMITTED;
        public int isolationMode = SessionTxConsts.TX_READ_COMMITTED;
        public int actionIndex;
        public long actionTimestamp;
        public long transactionTimestamp;
        public bool isTransaction;
        public bool isBatch;
        public volatile bool abortTransaction;
        public volatile bool redoAction;
        public EfzArrayList rowActionList;
        public volatile bool tempUnlocked;
        public OrderedHashSet waitingSessions;
        public OrderedHashSet tempSet;
        public CountUpDownLatch latch = new CountUpDownLatch();
        public  Statement currentStatement;
        public Statement lockStatement;

        // current settings
        public String zoneString;
        public int sessionTimeZoneSeconds;
        public int timeZoneSeconds;
        public bool isNetwork;
        private int currentMaxRows;
        private int sessionMaxRows;
        private ValueType lastIdentity = 0;
        private long sessionId;
        private bool script;

        // public connection
#if !SILVERLIGHT
        private DbConnection intConnection;
#else
        private object intConnection;
#endif

        // schema
        public QNameManager.QName currentSchema;
        public QNameManager.QName loggedSchema;

        // query processing
        public ParserCommand parser;
        public bool _isProcessingScript;
        public bool _isProcessingLog;
        public SessionContext sessionContext;
        public int resultMaxMemoryRows;

        //
        public SessionData sessionData;

        //
        public StatementManager statementManager;

        /** @todo 1.9.0 fredt - clarify in which circumstances Session has to disconnect */
        public Session getSession()
        {
            return this;
        }

        /**
         * Constructs a new Session object.
         *
         * @param  db the database to which this represents a connection
         * @param  user the initial user
         * @param  autocommit the initial autocommit value
         * @param  readonly the initial readonly value
         * @param  id the session identifier, as known to the database
         */
        public Session(Database db, User user, bool autocommit, bool _readonly,
                long id, String zoneString, int timeZoneSeconds)
        {

            sessionId = id;
            database = db;
            this.user = user;
            this.zoneString = zoneString;
            this.sessionTimeZoneSeconds = timeZoneSeconds;
            this.timeZoneSeconds = timeZoneSeconds;
            rowActionList = new EfzArrayList(true);
            waitingSessions = new OrderedHashSet();
            tempSet = new OrderedHashSet();
            _isAutoCommit = autocommit;
            _isReadOnly = _readonly;
            isolationMode = isolationModeDefault;
            sessionContext = new SessionContext(this);
            parser = new ParserCommand(this, new Scanner());

            setResultMemoryRowCount(database.getResultMaxMemoryRows());
            resetSchema();

            sessionData = new SessionData(database, this);
            statementManager = new StatementManager(database);
        }

        public void resetSchema()
        {
            loggedSchema = null;
            currentSchema = user.getInitialOrDefaultSchema();
        }

        /**
         *  Retrieves the session identifier for this Session.
         *
         * @return the session identifier for this Session
         */
        public long getId()
        {
            return sessionId;
        }

        /**
         * Closes this Session.
         */
        public void close()
        {
            lock (this)
            {
                if (isClosed())
                {
                    return;
                }

                rollback(false);

                try
                {
                    database.logger.writeToLog(this, Tokens.T_DISCONNECT);
                }
                catch (CoreException ) { }

                sessionData.closeAllNavigators();
                sessionData.persistentStoreCollection.clearAllTables();
                sessionData.closeResultCache();
                statementManager.reset();
                database.sessionManager.removeSession(this);
                database.checkPointIfLast();

                database = null;
                user = null;
                rowActionList = null;
                sessionContext.savepoints = null;
                intConnection = null;
                sessionContext = null;
                lastIdentity = null;
                _isClosed = true;
            }
        }

        /**
         * Retrieves whether this Session is closed.
         *
         * @return true if this Session is closed
         */
        public bool isClosed()
        {
            return _isClosed;
        }

        public void setIsolationDefault(int level)
        {
            lock (this)
            {
                if (level == SessionTxConsts.TX_READ_UNCOMMITTED)
                {
                    _isReadOnlyDefault = true;
                }

                if (level == isolationModeDefault)
                {
                    return;
                }

                isolationModeDefault = level;

                if (!isInMidTransaction())
                {
                    isolationMode = isolationModeDefault;
                }

                database.logger.writeToLog(this, getSessionIsolationSQL());
            }
        }

        /**
         * sets ISOLATION for the next transaction only
         */
        public void setIsolation(int level)
        {

            if (isInMidTransaction())
            {
                throw Error.error(ErrorCode.X_25001);
            }

            if (level == SessionTxConsts.TX_READ_UNCOMMITTED)
            {
                _isReadOnly = true;
            }

            isolationMode = level;

            if (isolationMode != isolationModeDefault)
            {
                database.logger.writeToLog(this, getTransactionIsolationSQL());
            }
        }

        public int getIsolation()
        {
            lock (this)
            {
                return isolationMode;
            }
        }

        /**
         * Setter for iLastIdentity attribute.
         *
         * @param  i the new value
         */
        public void setLastIdentity(ValueType i)
        {
            lastIdentity = i;
        }

        /**
         * Getter for iLastIdentity attribute.
         *
         * @return the current value
         */
        public ValueType getLastIdentity()
        {
            return lastIdentity;
        }

        /**
         * Retrieves the Database instance to which this
         * Session represents a connection.
         *
         * @return the Database object to which this Session is connected
         */
        public Database getDatabase()
        {
            return database;
        }

        /**
         * Retrieves the name, as known to the database, of the
         * user currently controlling this Session.
         *
         * @return the name of the user currently connected within this Session
         */
        public String getUsername()
        {
            return user.getNameString();
        }

        /**
         * Retrieves the User object representing the user currently controlling
         * this Session.
         *
         * @return this Session's User object
         */
        public User getUser()
        {
            return (User)user;
        }

        public Grantee getGrantee()
        {
            return user;
        }

        public Grantee getRole()
        {
            return role;
        }

        /**
         * Sets this Session's User object to the one specified by the
         * user argument.
         *
         * @param  user the new User object for this session
         */
        public void setUser(User user)
        {
            this.user = user;
        }

        public void setRole(Grantee role)
        {
            this.role = role;
        }

        public int getMaxRows()
        {
            return currentMaxRows;
        }

        public int getSQLMaxRows()
        {
            return sessionMaxRows;
        }

        /**
         * The SQL command SET MAXROWS n will override the Statement.setMaxRows(n)
         * until SET MAXROWS 0 is issued.
         *
         * NB this is dedicated to the SET MAXROWS sql statement and should not
         * otherwise be called. (fredt@users)
         */
        public void setSQLMaxRows(int rows)
        {
            currentMaxRows = sessionMaxRows = rows;
        }

        /**
         * Checks whether this Session's current User has the privileges of
         * the ADMIN role.
         */
        public void checkAdmin()
        {
            user.checkAdmin();
        }

        /**
         * This is used for reading - writing to existing tables.
         * @throws  CoreException
         */
        public void checkReadWrite()
        {

            if (_isReadOnly)
            {
                throw Error.error(ErrorCode.X_25006);
            }
        }

        /**
         * This is used for creating new database objects such as tables.
         * @throws  CoreException
         */
        public void checkDDLWrite()
        {

            checkReadWrite();

            if (_isProcessingScript || _isProcessingLog)
            {
                return;
            }
        }

        public long getActionTimestamp()
        {
            return actionTimestamp;
        }

        /**
         *  Adds a delete action to the row and the transaction manager.
         *
         * @param  table the table of the row
         * @param  row the deleted row
         * @throws  CoreException
         */
        public void addDeleteAction(Table table, Row row)
        {

            //        tempActionHistory.add("add delete action " + actionTimestamp);
            if (abortTransaction)
            {

                //            throw Error.error(ErrorCode.X_40001);
            }

            database.txManager.addDeleteAction(this, table, row);
        }

        public void addInsertAction(Table table, Row row)
        {

            //        tempActionHistory.add("add insert to transaction " + actionTimestamp);
            database.txManager.addInsertAction(this, table, row);

            // abort only after adding so that the new row gets removed from indexes
            if (abortTransaction)
            {

                //            throw Error.error(ErrorCode.X_40001);
            }
        }

        /**
         *  Setter for the autocommit attribute.
         *
         * @param  autocommit the new value
         * @throws  CoreException
         */
        public void setAutoCommit(bool autocommit)
        {
            lock (this)
            {
                if (_isClosed)
                {
                    return;
                }

                if (autocommit != _isAutoCommit)
                {
                    commit(false);

                    _isAutoCommit = autocommit;
                }
            }
        }

        public void beginAction(Statement cs)
        {

            actionIndex = rowActionList.size();

            database.txManager.beginAction(this, cs);

            //        tempActionHistory.add("beginAction ends " + actionTimestamp);
        }

        public void endAction(Result r)
        {

            //        tempActionHistory.add("endAction " + actionTimestamp);
            sessionData.persistentStoreCollection.clearStatementTables();

            if (r.isError())
            {
                sessionData.persistentStoreCollection.clearResultTables(
                    actionTimestamp);
                database.txManager.rollbackAction(this);
            }
            else
            {
                database.txManager.completeActions(this);
            }

            //        tempActionHistory.add("endAction ends " + actionTimestamp);
        }

        public bool hasLocks()
        {
            return false;
        }

        public void startTransaction()
        {
            database.txManager.beginTransaction(this);
        }

        public void startPhasedTransaction() { }

        /**
         * @todo - fredt - for two phased pre-commit - after this call, further
         * state changing calls should fail
         */
        public void prepareCommit()
        {
            lock (this)
            {
                if (_isClosed)
                {
                    throw Error.error(ErrorCode.X_08003);
                }

                if (!database.txManager.prepareCommitActions(this))
                {

                    //            tempActionHistory.add("commit aborts " + actionTimestamp);
                    rollback(false);

                    throw Error.error(ErrorCode.X_40001);
                }
            }
        }

        /**
         * Commits any uncommited transaction this Session may have open
         *
         * @throws  CoreException
         */
        public void commit(bool chain)
        {
            lock (this)
            {
                //        tempActionHistory.add("commit " + actionTimestamp);
                if (_isClosed)
                {
                    return;
                }

                if (!isTransaction)
                {
                    _isReadOnly = _isReadOnlyDefault;
                    isolationMode = isolationModeDefault;

                    return;
                }

                if (!database.txManager.commitTransaction(this))
                {

                    //            tempActionHistory.add("commit aborts " + actionTimestamp);
                    rollback(false);

                    throw Error.error(ErrorCode.X_40001);
                }

                endTransaction(true);

                if (database != null && database.logger.needsCheckpointReset())
                {
                    Statement checkpoint =
                        ParserCommand.getCheckpointStatement(database, false);

                    executeCompiledStatement(checkpoint, null);
                }
            }
        }

        /**
         * Rolls back any uncommited transaction this Session may have open.
         *
         * @throws  CoreException
         */
        public void rollback(bool chain)
        {
            lock (this)
            {
                //        tempActionHistory.add("rollback " + actionTimestamp);
                if (_isClosed)
                {
                    return;
                }

                if (!isTransaction)
                {
                    _isReadOnly = _isReadOnlyDefault;
                    isolationMode = isolationModeDefault;

                    return;
                }

                try
                {
                    database.logger.writeToLog(this, Tokens.T_ROLLBACK);
                }
                catch (CoreException ) { }

                database.txManager.rollback(this);
                endTransaction(false);
            }
        }

        private void endTransaction(bool commit)
        {

            sessionData.updateLobUsage(commit);
            sessionContext.savepoints.clear();
            sessionContext.savepointTimestamps.clear();
            rowActionList.clear();
            sessionData.persistentStoreCollection.clearTransactionTables();
            sessionData.closeAllTransactionNavigators();

            _isReadOnly = _isReadOnlyDefault;
            isolationMode = isolationModeDefault;
            lockStatement = null;
            /* debug 190
                    tempActionHistory.add("commit ends " + actionTimestamp);
                    tempActionHistory.clear();
            //*/
        }

        /**
         * @todo no-op in this implementation. To be implemented for connection pooling
         */
        public void resetSession()
        {
            lock (this)
            {
                throw Error.error(ErrorCode.X_0A000);
            }
        }

        /**
         *  Registers a transaction SAVEPOINT. A new SAVEPOINT with the
         *  name of an existing one replaces the old SAVEPOINT.
         *
         * @param  name name of the savepoint
         * @throws  CoreException if there is no current transaction
         */
        public void savepoint(String name)
        {
            lock (this)
            {
                int index = sessionContext.savepoints.getIndex(name);

                if (index != -1)
                {
                    sessionContext.savepoints.remove(name);
                    sessionContext.savepointTimestamps.remove(index);
                }

                sessionContext.savepoints.add(name,
                                              rowActionList.size());
                sessionContext.savepointTimestamps.addLast(actionTimestamp);

                try
                {
                    database.logger.writeToLog(this, getSavepointSQL(name));
                }
                catch (CoreException ) { }
            }
        }

        /**
         *  Performs a partial transaction ROLLBACK to savepoint.
         *
         * @param  name name of savepoint
         * @throws  CoreException
         */
        public void rollbackToSavepoint(String name)
        {
            lock (this)
            {
                if (_isClosed)
                {
                    return;
                }

                int index = sessionContext.savepoints.getIndex(name);

                if (index < 0)
                {
                    throw Error.error(ErrorCode.X_3B001, name);
                }

                database.txManager.rollbackSavepoint(this, index);

                try
                {
                    database.logger.writeToLog(this, getSavepointRollbackSQL(name));
                }
                catch (CoreException ) { }
            }
        }

        /**
         * Performs a partial transaction ROLLBACK of current savepoint level.
         *
         * @throws  CoreException
         */
        public void rollbackToSavepoint()
        {
            lock (this)
            {
                if (_isClosed)
                {
                    return;
                }

                String name = (String)sessionContext.savepoints.getKey(0);

                database.txManager.rollbackSavepoint(this, 0);

                try
                {
                    database.logger.writeToLog(this, getSavepointRollbackSQL(name));
                }
                catch (CoreException ) { }
            }
        }

        /**
         * Releases a savepoint
         *
         * @param  name name of savepoint
         * @throws  CoreException if name does not correspond to a savepoint
         */
        public void releaseSavepoint(String name)
        {
            lock (this)
            {
                // remove this and all later savepoints
                int index = sessionContext.savepoints.getIndex(name);

                if (index < 0)
                {
                    throw Error.error(ErrorCode.X_3B001, name);
                }

                while (sessionContext.savepoints.size() > index)
                {
                    sessionContext.savepoints.remove(sessionContext.savepoints.size()
                                                     - 1);
                    sessionContext.savepointTimestamps.removeLast();
                }
            }
        }

        /**
         * sets READ ONLY for next transaction only
         *
         * @param  readonly the new value
         */
        public void setReadOnly(bool _readonly)
        {

            if (!_readonly && database.databaseReadOnly)
            {
                throw Error.error(ErrorCode.DATABASE_IS_READONLY);
            }

            if (isInMidTransaction())
            {
                throw Error.error(ErrorCode.X_25001);
            }

            _isReadOnly = _readonly;
        }

        public void setReadOnlyDefault(bool _readonly)
        {
            lock (this)
            {
                if (!_readonly && database.databaseReadOnly)
                {
                    throw Error.error(ErrorCode.DATABASE_IS_READONLY);
                }

                _isReadOnlyDefault = _readonly;

                if (!isInMidTransaction())
                {
                    _isReadOnly = _isReadOnlyDefault;
                }
            }
        }

        /**
         *  Getter for readonly attribute.
         *
         * @return the current value
         */
        public bool isReadOnly()
        {
            return _isReadOnly;
        }

        public bool isReadOnlyDefault()
        {
            lock (this)
            {
                return _isReadOnlyDefault;
            }
        }

        /**
         *  Getter for autoCommit attribute.
         *
         * @return the current value
         */
        public bool isAutoCommit()
        {
            lock (this)
            {
                return _isAutoCommit;
            }
        }

        public int getStreamBlockSize()
        {
            lock (this)
            {
                return 512 * 1024;
            }
        }

        public bool isInMidTransaction()
        {
            return isTransaction;
        }

        /**
         *  A switch to set scripting on the basis of type of statement executed.
         *  Afterwards the method reponsible for logging uses
         *  isScripting() to determine if logging is required for the executed
         *  statement. (fredt@users)
         *
         * @param  script The new scripting value
         */
        public void setScripting(bool script)
        {
            this.script = script;
        }

        /**
         * Getter for scripting attribute.
         *
         * @return  scripting for the last statement.
         */
        bool isScripting()
        {
            return script;
        }

#if !SILVERLIGHT
        /**
         * Retrieves an public Connection object equivalent to the one
         * that created this Session.
         *
         * @return  public connection.
         */
        public DbConnection getInternalConnection()
        {

            if (intConnection == null)
            {
         //       intConnection = new EffiProzConnection(this); TODOx
            }

            return intConnection;
        }
#else
        public object getInternalConnection()
        {

            if (intConnection == null)
            {
                //       intConnection = new EffiProzConnection(this); TODOx
            }

            return intConnection;
        }

#endif

        // boucherb@users 20020810 metadata 1.7.2
        //----------------------------------------------------------------
        private long connectTime = DateTime.Now.Ticks;

        // more effecient for MetaData concerns than checkAdmin

        /**
         * Getter for admin attribute.
         *
         * @return the current value
         */
        public bool isAdmin()
        {
            return user.isAdmin();
        }

        /**
         * Getter for connectTime attribute.
         *
         * @return the value
         */
        public long getConnectTime()
        {
            return connectTime;
        }

        /**
         * Count of acctions in current transaction.
         *
         * @return the current value
         */
        public int getTransactionSize()
        {
            return rowActionList.size();
        }

        public long getTransactionTimestamp()
        {
            return transactionTimestamp;
        }

        public Statement compileStatement(String sql)
        {

            parser.reset(sql);

            Statement cs = parser.compileStatement();

            return cs;
        }

        /**
         * Executes the command encapsulated by the cmd argument.
         *
         * @param cmd the command to execute
         * @return the result of executing the command
         */
        public Result execute(Result cmd)
        {
            lock (this)
            {
                if (_isClosed)
                {
                    return Result.newErrorResult(Error.error(ErrorCode.X_08503));
                }

                int type = cmd.getType();
                int maxRows = cmd.getUpdateCount();

                currentMaxRows = sessionMaxRows;


                switch (type)
                {

                    case ResultConstants.LARGE_OBJECT_OP:
                        {
                            return performLOBOperation((ResultLob)cmd);
                        }
                    case ResultConstants.EXECUTE:
                        {
                            if (maxRows == -1)
                            {
                                currentMaxRows = 0;
                            }
                            else if (sessionMaxRows == 0)
                            {
                                currentMaxRows = maxRows;
                            }

                            Result result = executeCompiledStatement(cmd);

                            result = performPostExecute(cmd, result);

                            return result;
                        }
                    case ResultConstants.BATCHEXECUTE:
                        {
                            Result result = executeCompiledBatchStatement(cmd);

                            result = performPostExecute(cmd, result);

                            return result;
                        }
                    case ResultConstants.EXECDIRECT:
                        {
                            if (maxRows == -1)
                            {
                                currentMaxRows = 0;
                            }
                            else if (sessionMaxRows == 0)
                            {
                                currentMaxRows = maxRows;
                            }

                            Result result = executeDirectStatement(cmd);

                            result = performPostExecute(cmd, result);

                            return result;
                        }
                    case ResultConstants.BATCHEXECDIRECT:
                        {
                            Result result = executeDirectBatchStatement(cmd);

                            result = performPostExecute(cmd, result);

                            return result;
                        }
                    case ResultConstants.PREPARE:
                        {
                            Statement cs;

                            try
                            {
                                cs = statementManager.compile(this, cmd);
                            }
                            catch (Exception t)
                            {
                                String errorString = cmd.getMainString();

                                if (database.getProperties().getErrorLevel()
                                        == EffiProzDatabaseProperties.NO_MESSAGE)
                                {
                                    errorString = null;
                                }

                                return Result.newErrorResult(t, errorString);
                            }

                            cs.setGeneratedColumnInfo(cmd.getGeneratedResultType(),
                                                      cmd.getGeneratedResultMetaData());

                            Result result = Result.newPrepareResponse(cs);

                            if (cs.getType() == StatementTypes.SELECT_CURSOR
                                    || cs.getType() == StatementTypes.CALL)
                            {
                                sessionData.setResultSetProperties(cmd, result);
                            }

                            result = performPostExecute(cmd, result);

                            return result;
                        }
                    case ResultConstants.CLOSE_RESULT:
                        {
                            closeNavigator(cmd.getResultId());

                            return Result.updateZeroResult;
                        }
                    case ResultConstants.UPDATE_RESULT:
                        {
                            Result result = this.executeResultUpdate(cmd);

                            result = performPostExecute(cmd, result);

                            return result;
                        }
                    case ResultConstants.FREESTMT:
                        {
                            statementManager.freeStatement(cmd.getStatementID());

                            return Result.updateZeroResult;
                        }
                    case ResultConstants.GETSESSIONATTR:
                        {
                            int id = cmd.getStatementType();

                            return getAttributesResult(id);
                        }
                    case ResultConstants.SETSESSIONATTR:
                        {
                            return setAttributes(cmd);
                        }
                    case ResultConstants.ENDTRAN:
                        {
                            switch (cmd.getActionType())
                            {

                                case ResultConstants.TX_COMMIT:
                                    try
                                    {
                                        commit(false);
                                    }
                                    catch (Exception t)
                                    {
                                        return Result.newErrorResult(t);
                                    }
                                    break;

                                case ResultConstants.TX_COMMIT_AND_CHAIN:
                                    try
                                    {
                                        commit(true);
                                    }
                                    catch (Exception t)
                                    {
                                        return Result.newErrorResult(t);
                                    }
                                    break;

                                case ResultConstants.TX_ROLLBACK:
                                    rollback(false);
                                    break;

                                case ResultConstants.TX_ROLLBACK_AND_CHAIN:
                                    rollback(true);
                                    break;

                                case ResultConstants.TX_SAVEPOINT_NAME_RELEASE:
                                    try
                                    {
                                        String name = cmd.getMainString();

                                        releaseSavepoint(name);
                                    }
                                    catch (Exception t)
                                    {
                                        return Result.newErrorResult(t);
                                    }
                                    break;

                                case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK:
                                    try
                                    {
                                        rollbackToSavepoint(cmd.getMainString());
                                    }
                                    catch (Exception t)
                                    {
                                        return Result.newErrorResult(t);
                                    }
                                    break;
                            }

                            return Result.updateZeroResult;
                        }
                    case ResultConstants.SETCONNECTATTR:
                        {
                            switch (cmd.getConnectionAttrType())
                            {

                                case ResultConstants.SQL_ATTR_SAVEPOINT_NAME:
                                    try
                                    {
                                        savepoint(cmd.getMainString());
                                    }
                                    catch (Exception t)
                                    {
                                        return Result.newErrorResult(t);
                                    }
                                    break;

                                // case ResultConstants.SQL_ATTR_AUTO_IPD
                                //   - always true
                                // default: throw - case never happens
                            }

                            return Result.updateZeroResult;
                        }
                    case ResultConstants.REQUESTDATA:
                        {
                            return sessionData.getDataResultSlice(cmd.getResultId(),
                                                                  cmd.getUpdateCount(),
                                                                  cmd.getFetchSize());
                        }
                    case ResultConstants.DISCONNECT:
                        {
                            close();

                            return Result.updateZeroResult;
                        }
                    default:
                        {
                            return Result.newErrorResult(
                                Error.runtimeError(ErrorCode.U_S0500, "Session"));
                        }
                }
            }
        }

        private Result performPostExecute(Result command, Result result)
        {

            if (result.isData())
            {
                result = sessionData.getDataResultHead(command, result, isNetwork);
            }

            if (sqlWarnings != null && sqlWarnings.Count > 0)
            {
                CoreException[] warnings = getAndClearWarnings();

                result.addWarnings(warnings);
            }

            return result;
        }

        public RowSetNavigatorClient getRows(long navigatorId, int offset,
                                             int blockSize)
        {
            return sessionData.getRowSetSlice(navigatorId, offset, blockSize);
        }

        public void closeNavigator(long id)
        {
            lock (this)
            {
                sessionData.closeNavigator(id);
            }
        }

        public Result executeDirectStatement(Result cmd)
        {

            String sql = cmd.getMainString();
            EfzArrayList list;

            try
            {
                list = parser.compileStatements(sql, cmd.getStatementType());
            }
            catch (CoreException e)
            {
                return Result.newErrorResult(e);
            }

            Result result = null;

            for (int i = 0; i < list.size(); i++)
            {
                Statement cs = (Statement)list.get(i);
               
                cs.setGeneratedColumnInfo(cmd.getGeneratedResultType(),
                                          cmd.getGeneratedResultMetaData());

                result = executeCompiledStatement(cs, new object[0]);

                if (result.isError())
                {
                    break;
                }
            }

            return result;
        }

        public Result executeDirectStatement(String sql)
        {

            Statement cs = null;

            parser.reset(sql);

            try
            {
                cs = parser.compileStatement();
            }
            catch (CoreException e)
            {
                return Result.newErrorResult(e);
            }

            Result result = executeCompiledStatement(cs,
                new object[0]);

            return result;
        }

        public Result executeCompiledStatement(Statement cs, Object[] pvals)
        {

            Result r;

            if (abortTransaction)
            {
                rollback(false);

                return Result.newErrorResult(Error.error(ErrorCode.X_40001));
            }

            currentStatement = cs;

            if (cs.isAutoCommitStatement())
            {
                try
                {
                    if (isReadOnly())
                    {
                        throw Error.error(ErrorCode.X_25006);
                    }

                    /** special autocommit for backward compatibility */
                    commit(false);
                }
                catch (CoreException )
                {
                    database.logger.logInfoEvent("Exception at commit");
                }
            }

            if (!cs.isTransactionStatement())
            {
                r = cs.execute(this);
                currentStatement = null;

                return r;
            }

            while (true)
            {
                beginAction(cs);

                if (abortTransaction)
                {
                    rollback(false);

                    currentStatement = null;

                    return Result.newErrorResult(Error.error(ErrorCode.X_40001));
                }

                try
                {
                    latch.await();
                }
#if !SILVERLIGHT && !PLATFORM_COMPACTFRAMEWORK
                catch (AbandonedMutexException )
#else
                catch (Exception)
#endif
                {

                    // System.out.println("interrupted");
                }

                if (abortTransaction)
                {
                    rollback(false);

                    currentStatement = null;

                    return Result.newErrorResult(Error.error(ErrorCode.X_40001));
                }

                //        tempActionHistory.add("sql execute " + cs.sql + " " + actionTimestamp + " " + rowActionList.size());
                sessionContext.pushDynamicArguments(pvals);

                r = cs.execute(this);

                sessionContext.popDynamicArguments();

                lockStatement = currentStatement;

                //        tempActionHistory.add("sql execute end " + actionTimestamp + " " + rowActionList.size());
                endAction(r);

                if (abortTransaction)
                {
                    rollback(false);

                    currentStatement = null;

                    return Result.newErrorResult(Error.error(ErrorCode.X_40001));
                }

                if (redoAction)
                {
                    redoAction = false;
                    latch.await();
                }
                else
                {
                    break;
                }
            }

            if (sessionContext.depth == 0
                    && (_isAutoCommit || cs.isAutoCommitStatement()))
            {
                try
                {
                    if (r.isError())
                    {
                        rollback(false);
                    }
                    else
                    {
                        commit(false);
                    }
                }
                catch (Exception e)
                {
                    currentStatement = null;

                    return Result.newErrorResult(Error.error(ErrorCode.X_40001,
                            e));
                }
            }

            currentStatement = null;

            return r;
        }

        private Result executeCompiledBatchStatement(Result cmd)
        {

            long csid;
            Statement cs;
            int[] updateCounts;
            int count;

            csid = cmd.getStatementID();
            cs = statementManager.getStatement(this, csid);

            if (cs == null)
            {

                // invalid sql has been removed already
                return Result.newErrorResult(Error.error(ErrorCode.X_07501));
            }

            count = 0;

            RowSetNavigator nav = cmd.initialiseNavigator();

            updateCounts = new int[nav.getSize()];

            Result generatedResult = null;

            if (cs.hasGeneratedColumns())
            {
                generatedResult =
                    Result.newDataResult(cs.generatedResultMetaData());
            }

            Result error = null;

            isBatch = true;

            while (nav.hasNext())
            {
                Object[] pvals = (Object[])nav.getNext();
                Result _in = executeCompiledStatement(cs, pvals);

                // On the client side, iterate over the vals and throw
                // a BatchUpdateException if a batch status value of
                // esultConstants.EXECUTE_FAILED is encountered in the result
                if (_in.isUpdateCount())
                {
                    if (cs.hasGeneratedColumns())
                    {
                        RowSetNavigator navgen =
                            _in.getChainedResult().getNavigator();

                        while (navgen.hasNext())
                        {
                            Object generatedRow = navgen.getNext();

                            generatedResult.getNavigator().add(generatedRow);
                        }
                    }

                    updateCounts[count++] = _in.getUpdateCount();
                }
                else if (_in.isData())
                {

                    // FIXME:  we don't have what it takes yet
                    // to differentiate between things like
                    // stored procedure calls to methods with
                    // void return type and select statements with
                    // a single row/column containg null
                    updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO;
                }
                else if (_in.isError())
                {
                    updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count);
                    error = _in;

                    break;
                }
                else
                {
                    throw Error.runtimeError(ErrorCode.U_S0500, "Session");
                }
            }

            isBatch = false;

            sessionData.updateLobUsageForBatch();

            return Result.newBatchedExecuteResponse(updateCounts, generatedResult,
                    error);
        }

        private Result executeDirectBatchStatement(Result cmd) {

        int[] updateCounts;
        int   count;

        count = 0;

        RowSetNavigator nav = cmd.initialiseNavigator();

        updateCounts = new int[nav.getSize()];

        Result error = null;

        isBatch = true;

        while (nav.hasNext()) {
            Result   _in;
            Object[] data = (Object[]) nav.getNext();
            String   sql  = (String) data[0];

            try {
                _in = executeDirectStatement(sql);
            } catch (Exception t) {
                _in = Result.newErrorResult(t);

                // if (t instanceof OutOfMemoryError) {
                // System.gc();
                // }
                // "in" alread equals "err"
                // maybe test for OOME and do a gc() ?
                // t.printStackTrace();
            }

            if (_in.isUpdateCount()) {
                updateCounts[count++] = _in.getUpdateCount();
            } else if (_in.isData()) {

                // FIXME:  we don't have what it takes yet
                // to differentiate between things like
                // stored procedure calls to methods with
                // void return type and select statements with
                // a single row/column containg null
                updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO;
            } else if (_in.isError()) {
                updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count);
                error        = _in;

                break;
            } else {
                throw Error.runtimeError(ErrorCode.U_S0500, "Session");
            }
        }

        isBatch = false;

        sessionData.updateLobUsageForBatch();

        return Result.newBatchedExecuteResponse(updateCounts, null, error);
    }

        /**
         * Retrieves the result of executing the prepared statement whose csid
         * and parameter values/types are encapsulated by the cmd argument.
         *
         * @return the result of executing the statement
         */
        private Result executeCompiledStatement(Result cmd)
        {

            Statement cs = cmd.getStatement();

            if (cs.getCompileTimestamp()
                    < database.schemaManager.getSchemaChangeTimestamp())
            {
                long csid = cmd.getStatementID();

                cs = statementManager.getStatement(this, csid);

                if (cs == null)
                {

                    // invalid sql has been removed already
                    return Result.newErrorResult(Error.error(ErrorCode.X_07502));
                }
            }

            Object[] pvals = cmd.getParameterData();

            return executeCompiledStatement(cs, pvals);
        }

        /**
         * Retrieves the result of inserting, updating or deleting a row
         * from an updatable result.
         *
         * @return the result of executing the statement
         */
        private Result executeResultUpdate(Result cmd)
        {

            long id = cmd.getResultId();
            int actionType = cmd.getActionType();
            Result result = sessionData.getDataResult(id);

            if (result == null)
            {
                return Result.newErrorResult(Error.error(ErrorCode.X_24501));
            }

            Object[] pvals = cmd.getParameterData();
            SqlType[] types = cmd.metaData.columnTypes;
            StatementQuery statement = (StatementQuery)result.getStatement();
            QueryExpression qe = statement.queryExpression;
            Table baseTable = qe.getBaseTable();
            int[] columnMap = qe.getBaseTableColumnMap();

            sessionContext.rowUpdateStatement.setRowActionProperties(actionType,
                    baseTable, types, columnMap);

            Result resultOut =
                executeCompiledStatement(sessionContext.rowUpdateStatement, pvals);

            return resultOut;
        }

        // session DATETIME functions
            //long                  currentDateSCN;
            long                  currentTimestampSCN;
            long                  currentMillis;
            private TimestampData currentDate;
            private TimestampData currentTimestamp;
            private TimestampData localTimestamp;
            private TimeData      currentTime;
            private TimeData      localTime;

            /**
             * Returns the current date, unchanged for the duration of the current
             * execution unit (statement).<p>
             *
             * SQL standards require that CURRENT_DATE, CURRENT_TIME and
             * CURRENT_TIMESTAMP are all evaluated at the same point of
             * time in the duration of each SQL statement, no matter how long the
             * SQL statement takes to complete.<p>
             *
             * When this method or a corresponding method for CURRENT_TIME or
             * CURRENT_TIMESTAMP is first called in the scope of a system change
             * number, currentMillis is set to the current system time. All further
             * CURRENT_XXXX calls in this scope will use this millisecond value.
             * (fredt@users)
             */
            public  TimestampData getCurrentDate() {
                lock (this)
                {
                    resetCurrentTimestamp();

                    if (currentDate == null)
                    {
                        currentDate = (TimestampData)SqlType.SQL_DATE.getValue(currentMillis
                                / 1000, 0, getZoneSeconds());
                    }

                    return currentDate;
                }
            }

            /**
             * Returns the current time, unchanged for the duration of the current
             * execution unit (statement)
             */
            public TimeData getCurrentTime(bool withZone) {
                lock (this)
                {
                    resetCurrentTimestamp();

                    if (withZone)
                    {
                        if (currentTime == null)
                        {
                            long seconds = currentMillis / 1000L;
                            int nanos = (int)((currentMillis % 1000L) * 1000000);

                            currentTime = new TimeData(seconds, nanos, getZoneSeconds());
                        }

                        return currentTime;
                    }
                    else
                    {
                        if (localTime == null)
                        {
                            long seconds =                                
                                    (currentMillis + getZoneSeconds() * 1000) / 1000L;
                            int nanos = (int)((currentMillis % 1000L) * 1000000);

                            localTime = new TimeData(seconds, nanos, 0);
                        }

                        return localTime;
                    }
                }
            }

            /**
             * Returns the current timestamp, unchanged for the duration of the current
             * execution unit (statement)
             */
            public TimestampData getCurrentTimestamp(bool withZone) {
                lock (this)
                {
                    resetCurrentTimestamp();

                    if (withZone)
                    {
                        if (currentTimestamp == null)
                        {
                            int nanos = (int)((currentMillis % 1000) * 1000000 );

                            currentTimestamp = new TimestampData((currentMillis / 1000),
                                                                 nanos, getZoneSeconds());
                        }

                        return currentTimestamp;
                    }
                    else
                    {
                        if (localTimestamp == null)
                        {
                            int nanos = (int)((currentMillis % 1000) * 1000000);

                            localTimestamp = new TimestampData(currentMillis / 1000
                                                               + getZoneSeconds(), nanos,
                                                                   0);
                        }

                        return localTimestamp;
                    }
                }
            }

            private void resetCurrentTimestamp() {

                if (currentTimestampSCN != actionTimestamp) {
                    currentTimestampSCN = actionTimestamp;
                    currentMillis       = DateTime.UtcNow.Ticks/TimeSpan.TicksPerMillisecond;
                    currentDate         = null;
                    currentTimestamp    = null;
                    localTimestamp      = null;
                    currentTime         = null;
                    localTime           = null;
                }
            }

            public int getZoneSeconds() {
                return timeZoneSeconds;
            }

            public void setZoneSeconds(int seconds) {

                if (seconds == sessionTimeZoneSeconds) {
                    calendar = null;
                } else
                {
#if !PLATFORM_COMPACTFRAMEWORK
#if !SILVERLIGHT 
                    TimeZoneInfo zone = TimeZoneInfo.CreateCustomTimeZone("SessionZone",
                        new TimeSpan(seconds * TimeSpan.TicksPerSecond), "SessionZone", "SessionZone");
#else
                    TimeZoneInfo zone = TimeZoneInfo.Local;
#endif
#endif
               
                     
                    calendar        = new System.Globalization.GregorianCalendar();
                    
                    timeZoneSeconds = seconds;
                }
            }

        private Result getAttributesResult(int id)
        {

            Result r = Result.newSessionAttributesResult();
            Object[] data = r.getSingleRowData();

            data[SessionInfoTypeConsts.INFO_ID] = (id);

            switch (id)
            {

                case SessionInfoConsts.INFO_ISOLATION:
                    data[SessionInfoTypeConsts.INFO_INTEGER] =
                        (isolationMode);
                    break;

                case SessionInfoConsts.INFO_AUTOCOMMIT:
                    data[SessionInfoTypeConsts.INFO_BOOLEAN] =
                        (_isAutoCommit);
                    break;

                case SessionInfoConsts.INFO_CONNECTION_READONLY:
                    data[SessionInfoTypeConsts.INFO_BOOLEAN] =
                        (_isReadOnly);
                    break;

                case SessionInfoConsts.INFO_CATALOG:
                    data[SessionInfoTypeConsts.INFO_VARCHAR] =
                        database.getCatalogName().name;
                    break;
            }

            return r;
        }

        private Result setAttributes(Result r)
        {

            Object[] row = r.getSessionAttributes();
            int id = ((int)row[SessionInfoTypeConsts.INFO_ID]);

            try
            {
                switch (id)
                {

                    case SessionInfoConsts.INFO_AUTOCOMMIT:
                        {
                            bool value =
                                ((Boolean)row[SessionInfoTypeConsts.INFO_BOOLEAN]);


                            this.setAutoCommit(value);

                            break;
                        }
                    case SessionInfoConsts.INFO_CONNECTION_READONLY:
                        {
                            bool value =
                                ((Boolean)row[SessionInfoTypeConsts.INFO_BOOLEAN]);


                            this.setReadOnlyDefault(value);

                            break;
                        }
                    case SessionInfoConsts.INFO_ISOLATION:
                        {
                            int value =
                                ((int)row[SessionInfoTypeConsts.INFO_INTEGER]);

                            this.setIsolation(value);

                            break;
                        }
                    case SessionInfoConsts.INFO_CATALOG:
                        {
                            String value =
                                ((String)row[SessionInfoTypeConsts.INFO_VARCHAR]);

                            this.setCatalog(value);
                        }
                        break;
                }
            }
            catch (CoreException e)
            {
                return Result.newErrorResult(e);
            }

            return Result.updateZeroResult;
        }

        public Object getAttribute(int id)
        {
            lock (this)
            {

                switch (id)
                {

                    case SessionInfoConsts.INFO_ISOLATION:
                        return (isolationMode);

                    case SessionInfoConsts.INFO_AUTOCOMMIT:
                        return (_isAutoCommit);

                    case SessionInfoConsts.INFO_CONNECTION_READONLY:
                        return (_isReadOnly);

                    case SessionInfoConsts.INFO_CATALOG:
                        return database.getCatalogName().name;
                }

                return null;
            }
        }

        public void setAttribute(int id, Object _object)
        {

            lock (this)
            {
                switch (id)
                {

                    case SessionInfoConsts.INFO_AUTOCOMMIT:
                        {
                            bool value = ((Boolean)_object);

                            this.setAutoCommit(value);

                            break;
                        }
                    case SessionInfoConsts.INFO_CONNECTION_READONLY:
                        {
                            bool value = ((Boolean)_object);

                            this.setReadOnlyDefault(value);

                            break;
                        }
                    case SessionInfoConsts.INFO_ISOLATION:
                        {
                            int value = ((int)_object);

                            this.setIsolation(value);

                            break;
                        }
                    case SessionInfoConsts.INFO_CATALOG:
                        {
                            String value = ((String)_object);

                            this.setCatalog(value);
                            break;
                        }
                }
            }
        }

        // lobs
        public BlobDataID createBlob(long length) {

            long lobID = database.lobManager.createBlob(length);

            if (lobID == 0) {
                throw Error.error(ErrorCode.X_0F502);
            }

            sessionData.addToCreatedLobs(lobID);

            return new BlobDataID(lobID);
        }

        public ClobDataID createClob(long length) {

            long lobID = database.lobManager.createClob(length);

            if (lobID == 0) {
                throw Error.error(ErrorCode.X_0F502);
            }

            sessionData.addToCreatedLobs(lobID);

            return new ClobDataID(lobID);
        }

        public void registerResultLobs(Result result) {
            sessionData.registerLobForResult(result);
        }

        public virtual void allocateResultLob(ResultLob result, Stream inputStream) {
            sessionData.allocateLobForResult(result, inputStream);
        }

        Result performLOBOperation(ResultLob cmd) {

            long id        = cmd.getLobID();
            int  operation = cmd.getSubType();

            switch (operation) {

                case LobResultTypes.REQUEST_GET_LOB : {
                    return database.lobManager.getLob(this, id, cmd.getOffset(),
                                                      cmd.getBlockLength());
                }
                case LobResultTypes.REQUEST_GET_LENGTH : {
                    return database.lobManager.getLength(this, id);
                }
                case LobResultTypes.REQUEST_GET_BYTES : {
                    return database.lobManager.getBytes(
                        this, id, cmd.getOffset(), (int) cmd.getBlockLength());
                }
                case LobResultTypes.REQUEST_SET_BYTES : {
                    return database.lobManager.setBytes(this, id,
                                                        cmd.getByteArray(),
                                                        cmd.getOffset());
                }
                case LobResultTypes.REQUEST_GET_CHARS : {
                    return database.lobManager.getChars(
                        this, id, cmd.getOffset(), (int) cmd.getBlockLength());
                }
                case LobResultTypes.REQUEST_SET_CHARS : {
                    return database.lobManager.setChars(this, id, cmd.getOffset(),
                                                        cmd.getCharArray());
                }
                case LobResultTypes.REQUEST_TRUNCATE : {
                    return database.lobManager.truncate(this, id, cmd.getOffset());
                }
                case LobResultTypes.REQUEST_CREATE_BYTES :
                case LobResultTypes.REQUEST_CREATE_CHARS :
                case LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION :
                case LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION :
                default : {
                    throw Error.runtimeError(ErrorCode.U_S0500, "Session");
                }
            }
        }

        // DatabaseMetaData.getURL should work as specified for
        // public connections too.
        public String getInternalConnectionURL()
        {
            return DatabaseURL.S_URL_PREFIX + database.getURI();
        }

       public bool isProcessingScript()
        {
            return _isProcessingScript;
        }

        public bool isProcessingLog()
        {
            return _isProcessingLog;
        }

        // schema object methods
        public void setSchema(String schema)
        {
            currentSchema = database.schemaManager.getSchemaHsqlName(schema);
        }

        public void setCatalog(String catalog)
        {

            if (database.getCatalogName().name.Equals(catalog))
            {
                return;
            }

            throw Error.error(ErrorCode.X_3D000);
        }

        /**
         * If schemaName is null, return the current schema name, else return
         * the QNameManager.QName object for the schema. If schemaName does not exist,
         * throw.
         */
        public QNameManager.QName getSchemaQName(String name)
        {
            return name == null ? currentSchema
                                : database.schemaManager.getSchemaHsqlName(name);
        }

        /**
         * Same as above, but return string
         */
        public String getSchemaName(String name)
        {
            return name == null ? currentSchema.name
                                : database.schemaManager.getSchemaName(name);
        }

        public void setCurrentSchemaQName(QNameManager.QName name)
        {
            currentSchema = name;
        }

        public QNameManager.QName getCurrentSchemaHsqlName()
        {
            return currentSchema;
        }

        // session tables
        Table[] transitionTables = Table.emptyArray;

        public void setSessionTables(Table[] tables)
        {
            transitionTables = tables;
        }

        public Table findSessionTable(String name)
        {

            for (int i = 0; i < transitionTables.Length; i++)
            {
                if (name.Equals(transitionTables[i].getName().name))
                {
                    return transitionTables[i];
                }
            }

            return null;
        }

        //
        public int getResultMemoryRowCount()
        {
            return resultMaxMemoryRows;
        }

        public void setResultMemoryRowCount(int count)
        {

            if (database.logger.getTempDirectoryPath() != null)
            {
                if (count <= 0)
                {
                    count = int.MaxValue;
                }

                resultMaxMemoryRows = count;
            }
        }

        // warnings
        Queue<CoreException> sqlWarnings;

        public void addWarning(CoreException warning)
        {

            if (sqlWarnings == null)
            {
                sqlWarnings = new Queue<CoreException>();
            }

            if (sqlWarnings.Count > 9)
            {
                sqlWarnings.Dequeue();
            }

            if (sqlWarnings.Contains(warning))
            {
                return;
            }

            sqlWarnings.Enqueue(warning);
        }

        public CoreException[] getAndClearWarnings()
        {

            if (sqlWarnings == null)
            {
                return CoreException.emptyArray;
            }

            CoreException[] array = new CoreException[sqlWarnings.Count];

            array = sqlWarnings.ToArray();
            sqlWarnings.Clear();

            return array;
        }

        public CoreException getLastWarning()
        {

            if (sqlWarnings == null || sqlWarnings.Count == 0)
            {
                return null;
            }

            return (CoreException)sqlWarnings.Dequeue();
        }

        public void clearWarnings()
        {

            if (sqlWarnings != null)
            {
                sqlWarnings.Clear();
            }
        }

        // session zone
        System.Globalization.GregorianCalendar calendar;

        public System.Globalization.Calendar getCalendar()
        {

            if (calendar == null)
            {

                calendar = new System.Globalization.GregorianCalendar();
            }

            return calendar;
        }

        // services
        Scanner secondaryScanner;
        //string simpleDateFormat;
        //string simpleDateFormatGMT;
        Random randomGenerator = new Random();

        //
        public double random(int seed)
        {

            randomGenerator = new Random(seed);

            return randomGenerator.NextDouble();
        }

        public double random()
        {
            return randomGenerator.NextDouble();
        }

        public Scanner getScanner()
        {

            if (secondaryScanner == null)
            {
                secondaryScanner = new Scanner();
            }

            return secondaryScanner;
        }

        //public System.Globalization.DateTimeFormatInfo getSimpleDateFormatGMT() {

        //    if (simpleDateFormatGMT == null)
        //    {
        //        simpleDateFormatGMT = new SimpleDateFormat("MMMM", Locale.ENGLISH);

        //        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));

        //        simpleDateFormatGMT.setCalendar(cal);
        //    }
           

        //    return System.Globalization.DateTimeFormatInfo;
        //}

        // SEQUENCE current values
        public void logSequences() {

            OrderedHashSet set = sessionData.sequenceUpdateSet;

            if (set == null || set.isEmpty()) {
                return;
            }

            for (int i = 0, size = set.size(); i < size; i++) {
                NumberSequence sequence = (NumberSequence) set.get(i);

                database.logger.writeSequenceStatement(this, sequence);
            }

            sessionData.sequenceUpdateSet.clear();
        }

        //
        static String getSavepointSQL(String name)
        {

            StringBuilder sb = new StringBuilder(Tokens.T_SAVEPOINT);

            sb.Append(' ').Append('"').Append(name).Append('"');

            return sb.ToString();
        }

        static String getSavepointRollbackSQL(String name)
        {

            StringBuilder sb = new StringBuilder();

            sb.Append(Tokens.T_ROLLBACK).Append(' ').Append(Tokens.T_TO).Append(
                ' ');
            sb.Append(Tokens.T_SAVEPOINT).Append(' ');
            sb.Append('"').Append(name).Append('"');

            return sb.ToString();
        }

        public String getStartTransactionSQL()
        {

            StringBuilder sb = new StringBuilder();

            sb.Append(Tokens.T_START).Append(' ').Append(Tokens.T_TRANSACTION);

            if (isolationMode != isolationModeDefault)
            {
                sb.Append(' ');
                appendIsolationSQL(sb, isolationMode);
            }

            return sb.ToString();
        }

        String getTransactionIsolationSQL()
        {

            StringBuilder sb = new StringBuilder();

            sb.Append(Tokens.T_SET).Append(' ').Append(Tokens.T_TRANSACTION);
            sb.Append(' ');
            appendIsolationSQL(sb, isolationMode);

            return sb.ToString();
        }

        String getSessionIsolationSQL()
        {

            StringBuilder sb = new StringBuilder();

            sb.Append(Tokens.T_SET).Append(' ').Append(Tokens.T_SESSION);
            sb.Append(' ').Append(Tokens.T_CHARACTERISTICS).Append(' ');
            sb.Append(Tokens.T_AS).Append(' ');
            appendIsolationSQL(sb, isolationModeDefault);

            return sb.ToString();
        }

        static void appendIsolationSQL(StringBuilder sb, int isolationLevel)
        {

            sb.Append(Tokens.T_ISOLATION).Append(' ');
            sb.Append(Tokens.T_LEVEL).Append(' ');

            switch (isolationLevel)
            {

                case SessionTxConsts.TX_READ_UNCOMMITTED:
                case SessionTxConsts.TX_READ_COMMITTED:
                    sb.Append(Tokens.T_READ).Append(' ');
                    sb.Append(Tokens.T_COMMITTED);
                    break;

                case SessionTxConsts.TX_REPEATABLE_READ:
                case SessionTxConsts.TX_SERIALIZABLE:
                    sb.Append(Tokens.T_SERIALIZABLE);
                    break;
            }
        }

        String getSetSchemaStatement()
        {
            return "SET SCHEMA " + currentSchema.statementName;
        }

    }
}